package org.javaexpert.spaces.masterworker.masterexample;

import net.jini.core.entry.UnusableEntryException;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.transaction.Transaction;
import net.jini.core.transaction.TransactionException;
import net.jini.core.transaction.TransactionFactory;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.space.JavaSpace;
import org.javaexpert.spaces.masterworker.TaskBag;
import org.javaexpert.spaces.masterworker.TaskGroup;
import org.javaexpert.spaces.util.SpaceUtils;
import org.javaexpert.spaces.util.TransactionTemplate;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class Master2 {

    private JavaSpace space;
    private TransactionManager mgr;
    private static final int ADD_COUNT = 5;
    private TaskBag taskBag;


    public static void main(String[] args) throws Exception {
        Master2 master = new Master2();
        master.startComputing();
    }


    private void startComputing() throws Exception {
        space = SpaceUtils.findSpace("mySpace", "localhost");
        mgr = SpaceUtils.findTransactionManager("localhost");
        taskBag = new TaskBag("bag1", space, new TransactionTemplate(mgr));

        List<Callable> tasks = generateTasks();
        TaskGroup group = taskBag.submit(tasks);
        collectResults(group);
    }


    private List<Callable> generateTasks() throws RemoteException, UnusableEntryException, TransactionException, InterruptedException, LeaseDeniedException {
        List<Callable> tasks = new ArrayList<Callable>();
        for (int i = 0; i < ADD_COUNT; i++) {
            try {
                tasks.add(new AddCallable(i));
                tasks.add(new MultCallable(i));
                System.out.print(".");

            } catch (Exception e) {
                System.out.println("i1=" + i);
                e.printStackTrace();
            }
        }
        return tasks;
    }


    protected void collectResults(TaskGroup futures) throws ExecutionException, InterruptedException {
        int i = 0;
        while (true) {
            System.out.printf("res_%d:%d\n", (i++), futures.take());
        }
    }


    public Transaction getTransaction(long leaseTime) {
        try {
            Transaction.Created created =
                    TransactionFactory.create(mgr, leaseTime);
            return created.transaction;
        } catch (RemoteException e) {
            e.printStackTrace();
            return null;
        } catch (LeaseDeniedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static class AddCallable implements Callable<Integer>, Serializable {
        public Integer i1;
        private Random random = new Random(System.currentTimeMillis());

        public AddCallable(int i1) {
            this.i1 = i1;
        }

        public Integer call() throws Exception {
            randomSleep();
            switch (random.nextInt(500)) {
                case 4:
                    throw new RuntimeException("-----exception----");
                case 5:
                    throw new Error("-----ERROR------");
                case 6:
                    System.exit(-1);
                case 7:
                    return null;
            }


            return i1 + i1;
        }

        private void randomSleep() {
            try {
                Thread.sleep(random.nextInt(10) * 100);
            } catch (InterruptedException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
    }

    public static class MultCallable implements Callable<Integer>, Serializable {
        public Integer i1;


        public MultCallable(int i1) {
            this.i1 = i1;
        }

        public Integer call() throws Exception {
            return i1 * i1;
        }
    }


}
